package edu.stanford.bmir.protege.web.server.stkosutil.cutter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Stack;

import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.EntityType;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.RemoveAxiom;
import org.semanticweb.owlapi.vocab.OWLRDFVocabulary;

import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.ItemSelector;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.MatchLevel;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.MetaItemList;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.UtilConstants;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingMode;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingScope;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.mapper.MappingOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.mapper.MappingResult;
import edu.stanford.bmir.protege.web.server.owlapi.AssertedClassHierarchyProvider;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProject;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProjectManager;
import edu.stanford.bmir.protege.web.server.owlapi.RenderingManager;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.OwlClassHolder;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.OwlClassHolderComparator;
import edu.stanford.bmir.protege.web.shared.project.ProjectId;
import edu.stanford.bmir.protege.web.shared.user.UserId;


public class ClassCutter {
		
		 /****
		  *The following functions are copied from OntologyServiceOWLAPIImpl as they are not visible
		  * 
		  */
		 private OWLAPIProject getProject(String projectName) {
		        if (projectName == null) {
		            throw new NullPointerException("projectName must not be null");
		        }
		        ProjectId projectId = ProjectId.get(projectName);
		        // TODO: Log
		        return getProject(projectId);
		    }

		    /**
		     * Gets the OWLAPIProject for a given {@link ProjectId}.If a project with the specified id exists then that project
		     * will be returned, otherwise, a fresh project will be created and that fresh project returned.
		     * @param projectId The id of the project.
		     * @return The OWL API project. Not <code>null</code>.
		     */
		    private OWLAPIProject getProject(ProjectId projectId) {
		        OWLAPIProjectManager pm = OWLAPIProjectManager.getProjectManager();
		        return pm.getProject(projectId);
		    }
		 //*******************   
		
		    
		 private List<OwlClassHolder> getSubclasses(OWLAPIProject project, String className) {
		        if (className == null) {
		            return Collections.emptyList();
		        }
		        List<OwlClassHolder> result = new ArrayList<OwlClassHolder>();
		       
		        RenderingManager rm = project.getRenderingManager();
		        AssertedClassHierarchyProvider hierarchyProvider = project.getClassHierarchyProvider();
		        OWLClass cls = rm.getEntity(className, EntityType.CLASS);

		        boolean checkForDeprecated = project.getRootOntology().containsAnnotationPropertyInSignature(OWLRDFVocabulary.OWL_DEPRECATED.getIRI());
		        for (OWLClass subclass : new ArrayList<OWLClass>(hierarchyProvider.getChildren(cls))) {
		            boolean deprecated = false;
//		            if(checkForDeprecated) {
//		                deprecated = project.isDeprecated(subclass);
//		            }
		        //if (!deprecated) {
		              //  Set<OWLClass> children = hierarchyProvider.getChildren(subclass);
		              //  int subClassSubClassesCount = children.size();
		                String browserText = rm.getBrowserText(subclass);
		                String name = subclass.getIRI().toString();
		                OwlClassHolder classHolder = new OwlClassHolder();;
		                classHolder.setBrowserText(browserText);
		                classHolder.setName(name);
		                classHolder.setOwlClass(subclass);
		                //data.setDeprecated(deprecated);

		                //data.setValueType(ValueType.Cls);
		                result.add(classHolder);
//		            }
		        }
		        Collections.sort(result, new Comparator<OwlClassHolder>() {
		            public int compare(OwlClassHolder o1, OwlClassHolder o2) {
//		                if(o1.isDeprecated()) {
//		                    if(!o2.isDeprecated()) {
//		                        return 1;
//		                    }
//		                }
//		                else if(o2.isDeprecated()) {
//		                    return -1;
//		                }
		                String browserText1 = o1.getBrowserText();
		                String browserText2 = o2.getBrowserText();
		                if(browserText1.startsWith("'")) {
		                    browserText1 = browserText1.substring(1);
		                }
		                if(browserText2.startsWith("'")) {
		                    browserText2 = browserText2.substring(1);
		                }
		                return browserText1.compareToIgnoreCase(browserText2);
		            }
		        });
		        return result;
		    }
		 	private List<OwlClassHolder> loadAllClassList(OWLAPIProject project, String topClassName){
		 		 List<OwlClassHolder> result = new ArrayList<OwlClassHolder>();
		 		 Stack<OwlClassHolder> sClassHolders = new Stack<OwlClassHolder>();
		 		 OwlClassHolder root = this.getTopClass(project, topClassName);
		 		 sClassHolders.push(root);
		 		 while (!sClassHolders.empty()){
		 			 OwlClassHolder currentClass = sClassHolders.pop();
		 			 List<OwlClassHolder> children = this.getSubclasses(project, currentClass.getName());
		 			 Collections.reverse(children);
		 			 for (OwlClassHolder child: children){
		 				 sClassHolders.push(child);
		 			 }
		 			 result.add(currentClass);		 
		 		 }
		 		 return result;
		 	}
		 
		 	private OwlClassHolder getTopClass(OWLAPIProject project, String topClassName) {
		 	     OwlClassHolder holder = new OwlClassHolder();
		 	     RenderingManager rm = project.getRenderingManager();
		 	     OWLClass topClass = null;
		 	     if (topClassName==null){
		 	    	 topClass = project.getDataFactory().getOWLThing();
		 	     }else{
		 	    	 topClass = rm.getEntity(topClassName, EntityType.CLASS);
		 	     }
		 	     String browserText = rm.getBrowserText(topClass);
	             String name = topClass.getIRI().toString();
	             holder.setBrowserText(browserText);
	             holder.setName(name);
		 	     holder.setOwlClass(topClass);
		 	     return holder;
		 	}
		 	public CuttingResult cutOntology(String projectName, CuttingOptions options, UserId userId){
		 		String refProjectName = options.getRefProjectName();
		 		CuttingResult result = new CuttingResult();
		 		List<OwlClassHolder> oClassList = null;
		 	
		 		List<OWLOntologyChange> oChangeList = new ArrayList<OWLOntologyChange>();
		 		
		 		OWLAPIProject currProject = this.getProject(projectName);
		 		//OWLAPIProject refProject = this.getProject(refProjectName);
		 		String topClassName = null;
		 		if (options.getCuttingScope()==CuttingScope.SELECTED_CLASS_TREE){
		 			topClassName = options.getSelectedEntityName();
		 		}
		 		
		 		oClassList= this.loadAllClassList(currProject, topClassName);
		 		int cutNumber = this.fillCuttingFlagAxiom(currProject, oClassList, oChangeList,options);
		 		currProject.applyChanges(userId, oChangeList, "本体自动裁切");
		 		result.setCutClassNumber(cutNumber);
		 		result.setTotalClassNumber(oClassList.size());
		 		return result;
		 	}
		 	private int fillCuttingFlagAxiom(OWLAPIProject project, List<OwlClassHolder> classList, List<OWLOntologyChange> changeList, CuttingOptions options){
		 		int count=0;
		 		OWLDataFactory df = project.getDataFactory();
		 		for (OwlClassHolder owlClassHolder: classList){
		 			OWLClass owlClass = owlClassHolder.getOwlClass(); 
		 			boolean hasMappingNo=false;
		 			
		 			for (OWLAnnotation annotation : owlClass.getAnnotations(project.getRootOntology(), df.getRDFSComment())) {
			    		 if (annotation.getValue() instanceof OWLLiteral) {
				    		 OWLLiteral commentValue = (OWLLiteral) annotation.getValue();
				    		 String value = commentValue.getLiteral();
				    		  if (MetaItemList.isPreProjectMappingFlag(value, options.getRefProjectName())){
				    			  hasMappingNo = true;
				    		  }
				    		  if (value.startsWith(UtilConstants.FLAG_CUTTING)){			  
				    			  OWLAxiom oldAx = df.getOWLAnnotationAssertionAxiom(
				    						 owlClass.getIRI(), annotation);
					 			  RemoveAxiom removeAxiom = new RemoveAxiom(project.getRootOntology(),oldAx);
					 				 changeList.add(removeAxiom);
				    		  }
				    			  				    		  }
			    	}//end for
		 			
		 			if (qualifiedByCondition(owlClassHolder, hasMappingNo, options)){
		 				String annotaionValue =  UtilConstants.FLAG_CUTTING + ": " + "Y"; 
		 				OWLAnnotation commentAnno = df.getOWLAnnotation(
			 					 df.getRDFSComment(),
			 					 df.getOWLLiteral(annotaionValue, "en"));
			 			 OWLAxiom ax = df.getOWLAnnotationAssertionAxiom(
			 					 owlClass.getIRI(), commentAnno);
			 			 AddAxiom addAxiom =  new AddAxiom(project.getRootOntology(), ax);	 
			 			 changeList.add(addAxiom); 
			 			 count++;
		 			}
		 			
		 		}//end for
		 		return count;	
		 	}
		 	private boolean qualifiedByCondition(OwlClassHolder classHolder, boolean hasMappingNo, CuttingOptions options){
		 		if (options.getCuttingMode()==CuttingMode.REF_PROJECT){
		 			boolean qualified =  (hasMappingNo && options.getItemSelector()==ItemSelector.MATCHED_ITEMS)
	 					|| (hasMappingNo==false && options.getItemSelector()==ItemSelector.NOT_MATCHED_ITEMS);
		 			return qualified;
		 		}else{
		 			boolean matched = false;
		 			String search = options.getSearch();
		 			String entityName = classHolder.getBrowserText();
		 			if (search.compareTo("*")==0){
		 				return options.getItemSelector()==ItemSelector.MATCHED_ITEMS;
		 			}
		 			if (options.getMatchLevel()==MatchLevel.CASE_SENSITIVE){
		 				matched = entityName.compareTo(search)==0;
		 			}else if (options.getMatchLevel()==MatchLevel.CASE_INSENSITIVE){
		 				matched = entityName.compareToIgnoreCase(search)==0;
		 			}else if (options.getMatchLevel()==MatchLevel.INCLUSIVE){
		 				search = search.toLowerCase();
		 				entityName = entityName.toLowerCase();
		 				if (entityName.indexOf(search)!=-1){
		 					matched=true;
		 				}else{
		 					matched=false;
		 				}
		 			}
		 			if (options.getItemSelector()==ItemSelector.MATCHED_ITEMS){
		 				return matched;
		 			}else{
		 				return !matched;
		 			}
		 		}
		 	}
		
		 	
	}
